/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package project1;

import java.util.ArrayList;

/**
 *
 * @author maciej
 */
public class Scene {

    private double xAngle = 0.0;
    private double yAngle = 0.0;
    private double zAngle = 0.0;
    private int xPos = 0;
    private int yPos = 0;
    private int zPos = 0;
    private int width = 500;
    private int height = 500;
    private int xd = width / 2;
    private int yd = height / 2;
    private int VPD = 500;
    private ArrayList<Line3D> sceneLines = new ArrayList<Line3D>();

    public Scene() {
        //Box 1
        sceneLines.addAll((new Object3D(250.0, 200.0, 1250.0, 250.0, 500.0, 250.0)).getObject3D());

        //Box 2
        sceneLines.addAll((new Object3D(200.0, -150.0, 1250.0, 250.0, 500.0, 250.0)).getObject3D());

    }

    public ArrayList<Line2D> getProjection() {
        double s, cosx, sinx, cosy, siny, cosz, sinz, dx, dy, dz;
        int rx, ry, rz, bx, by;
        ArrayList<Line3D> lines3d;
        ArrayList<Line2D> rLines = new ArrayList<Line2D>();

        /*lines3d = new ArrayList<Line3D>();
        for (Line3D l : sceneLines) {
            lines3d.add(new Line3D(l.start.clone(), l.end.clone()));
        }*/
        lines3d = sceneLines;

        for (Line3D l : lines3d) {
            Line2D l2;
            //move(l.start);
            //move(l.end);
            rotate(l.start);
            rotate(l.end);
            l2 = project(l);
            if (l2 != null) {
                rLines.add(l2);
            }
        }
        xAngle = yAngle = zAngle = 0.0;
        xPos = yPos = zPos = 0;
        return rLines;
    }

    public void changeXAngle(double x) {
        xAngle = x;
    }

    public void changeYAngle(double y) {
        yAngle = y;
    }

    public void changeZAngle(double z) {
        zAngle = z;
    }

    public void changeXPosition(int x) {
        xPos = x;
    }

    public void changeYPosition(int y) {
        yPos = y;
    }

    public void changeZPosition(int z) {
        zPos = z;
    }

    public void zoom(int s) {
        VPD += s;
    }

    private Line2D project(Line3D l) {
        boolean bp1 = l.start.z >= VPD;
        boolean bp2 = l.end.z >= VPD;
        if (bp1 && !bp2) {
            if (l.start.z != VPD) {
                return changeVisPoint(l.start, l.end);
            } else {
                l.end.set(l.start);
            }
        } else if (!bp1 && bp2) {
            if (l.start.z != VPD) {
                return changeVisPoint(l.end, l.start);
            } else {
                l.start.set(l.end);
            }
        } else if (!bp1 && !bp2) {
            return null;
        }
        double k1 = VPD / (l.start.z);
        double k2 = VPD / (l.end.z);
        return new Line2D(new Point2D((int) (l.start.x * k1 + xd), (int) (yd - l.start.y * k1)),
                new Point2D((int) (l.end.x * k2 + xd), (int) (yd - l.end.y * k2)));
    }

    private void move(Point3D p){
        p.set(new Point3D(p.x+xPos, p.y+yPos, p.z+zPos));
    }
    /**
     * Rotate and move point with rotate/tranlation Matrices 
     * @param p 
     */
    private void rotate(Point3D p) {
        //X
        double x, y, z;
        double sinx = java.lang.Math.sin(xAngle);
        double cosx = java.lang.Math.cos(xAngle);
        double siny = java.lang.Math.sin(yAngle);
        double cosy = java.lang.Math.cos(yAngle);
        double sinz = java.lang.Math.sin(zAngle);
        double cosz = java.lang.Math.cos(zAngle);
        double r[][] = new double[4][4];
        double r2[][] = new double[4][4];
        double t[][] = new double[4][4];
        //OX
        r[0][0] = 1;
        r[1][1] = cosx;
        r[1][2] = sinx;
        r[2][1] = -sinx;
        r[2][2] = cosx;
        r[3][3] = 1;
        //x = p.x;
        //y = cosx * p.y - sinx * p.z;
        //z = sinx * p.y + cosx * p.z;
        //OY
        r2[0][0] = cosy;
        r2[0][2] = -siny;
        r2[1][1] = 1;
        r2[2][0] = siny;
        r2[2][2] = cosy;
        r2[3][3] = 1;
        //x = x * cosy + z * siny;
        //z = -siny * x + z * cosy;
        multiply(r,r2);
        //OZ
        r2[0][0] = cosz;
        r2[0][1] = sinz;
        r2[0][2] = 0;
        r2[1][0] = -sinz;
        r2[1][1] = cosz;
        r2[2][0] = 0;
        r2[2][2] = 1;
        r2[3][3] = 1;
        multiply(r,r2);
        //x = x * cosz - y * sinz;
        //y = x * sinz + y * cosz;
        t[0][0] = 1;
        t[1][1] = 1;
        t[2][2] = 1;
        t[3][3] = 1;
        t[3][2] = zPos;
        t[3][1] = yPos;
        t[3][0] = xPos;
        multiply(r,t);
        x = p.x*r[0][0]+p.y*r[1][0]+p.z*r[2][0]+r[3][0];
        y = p.x*r[0][1]+p.y*r[1][1]+p.z*r[2][1]+r[3][1];
        z = p.x*r[0][2]+p.y*r[1][2]+p.z*r[2][2]+r[3][2];
        p.set(new Point3D(x, y, z));
    }

    private Line2D changeVisPoint(Point3D p1, Point3D p2) {
        double Z = p1.z - p2.z;
        double z = p1.z - VPD;
        double x, y, k;
        k = z / Z;
        x = p1.x + ((p2.x - p1.x) * k);
        y = p1.y + ((p2.y - p1.y) * k);
        double k2 = VPD / (p1.z);

        return new Line2D(new Point2D((int) (p1.x * k2 + xd), (int) (yd - p1.y * k2)),
                new Point2D((int) (xd + x), (int) (yd - y)));
    }

    private void multiply(double[][] r, double[][] r2) {
        double x[][] = new double[4][4];
        for (int w=0; w<4; w++) {
            for (int k=0; k<4; k++) {
                x[w][k] = r[w][0]*r2[0][k]+r[w][1]*r2[1][k]+r[w][2]*r2[2][k]+r[w][3]*r2[3][k];
            }
        }
        for (int w=0; w<4; w++) {
            System.arraycopy(x[w], 0, r[w], 0, 4);
       }
    }
}
